let crypto = require('crypto');
let config = require('./config/config');
/**
 * Create token by uid. Encrypt uid and timestamp to get a token.
 *
 * @param  {String} uid user id
 * @param  {String|Number} timestamp
 * @param  {String} pwd encrypt password
 * @return {String}     token string
 */
let base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);

module.exports.stringToByte = function (str) {
    let bytes = new Array();
    let len, c;
    len = str.length;
    for (let i = 0; i < len; i++) {
        c = str.charCodeAt(i);
        if (c >= 0x010000 && c <= 0x10FFFF) {
            bytes.push(((c >> 18) & 0x07) | 0xF0);
            bytes.push(((c >> 12) & 0x3F) | 0x80);
            bytes.push(((c >> 6) & 0x3F) | 0x80);
            bytes.push((c & 0x3F) | 0x80);
        } else if (c >= 0x000800 && c <= 0x00FFFF) {
            bytes.push(((c >> 12) & 0x0F) | 0xE0);
            bytes.push(((c >> 6) & 0x3F) | 0x80);
            bytes.push((c & 0x3F) | 0x80);
        } else if (c >= 0x000080 && c <= 0x0007FF) {
            bytes.push(((c >> 6) & 0x1F) | 0xC0);
            bytes.push((c & 0x3F) | 0x80);
        } else {
            bytes.push(c & 0xFF);
        }
    }
    return bytes;
}


module.exports.byteToString = function (arr) {
    if (typeof arr === 'string') {
        return arr;
    }
    let str = '',
        _arr = arr;
    for (let i = 0; i < _arr.length; i++) {
        let one = _arr[i].toString(2),
            v = one.match(/^1+?(?=0)/);
        if (v && one.length == 8) {
            let bytesLength = v[0].length;
            let store = _arr[i].toString(2).slice(7 - bytesLength);
            for (let st = 1; st < bytesLength; st++) {
                store += _arr[st + i].toString(2).slice(2);
            }
            str += String.fromCharCode(parseInt(store, 2));
            i += bytesLength - 1;
        } else {
            str += String.fromCharCode(_arr[i]);
        }
    }
    return str;
}

module.exports.strToHexCharCode = function (str) {
    if (str === "")
        return "";

    let hexCharCode = [];
    hexCharCode.push("0x");
    for (let i = 0; i < str.length; i++) {
        hexCharCode.push((str.charCodeAt(i)).toString(16));
    }
    return hexCharCode.join("");
}

module.exports.hexCharCodeToStr = function (hexCharCodeStr) {
    let trimedStr = hexCharCodeStr.trim();
    let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
    let len = rawStr.length; if (len % 2 !== 0) { return ""; } let curCharCode;
    let resultStr = [];
    for (let i = 0; i < len; i = i + 2) {
        curCharCode = parseInt(rawStr.substr(i, 2), 16);// ASCII Code Value
        resultStr.push(String.fromCharCode(curCharCode));
    }
    return resultStr.join("");
}

module.exports.base64encode = function (str) {
    let out, i, len;
    let c1, c2, c3;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt((c1 & 0x3) << 4);
            out += "==";
            break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
            out += base64EncodeChars.charAt((c2 & 0xF) << 2);
            out += "=";
            break;
        }
        c3 = str.charCodeAt(i++);
        out += base64EncodeChars.charAt(c1 >> 2);
        out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += base64EncodeChars.charAt(c3 & 0x3F);
    }
    return out;
};
/**
 * base64解码
 * @param {Object} str
 */
module.exports.base64decode = function (str) {
    let c1, c2, c3, c4;
    let i, len, out;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        /* c1 */
        do {
            c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        }
        while (i < len && c1 == -1);
        if (c1 == -1)
            break;
        /* c2 */
        do {
            c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        }
        while (i < len && c2 == -1);
        if (c2 == -1)
            break;
        out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
        /* c3 */
        do {
            c3 = str.charCodeAt(i++) & 0xff;
            if (c3 == 61)
                return out;
            c3 = base64DecodeChars[c3];
        }
        while (i < len && c3 == -1);
        if (c3 == -1)
            break;
        out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
        /* c4 */
        do {
            c4 = str.charCodeAt(i++) & 0xff;
            if (c4 == 61)
                return out;
            c4 = base64DecodeChars[c4];
        }
        while (i < len && c4 == -1);
        if (c4 == -1)
            break;
        out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
    }
    return out;
};

module.exports.encodeRsa = function (content, privateKey) {
    let cipher = crypto.createSign('RSA-SHA1');
    let enc = cipher.update(content);
    let sig = cipher.sign(privateKey, 'base64');
    let b = new Buffer(sig);
    //let s = b.toString('base64');
    //console.log(sig);
    return sig;
};

//crypto.createHmac('sha1', app_secret).update('待加密字串').digest('hex');   //16进制

module.exports.encodeSha1 = function (content, secret) {
    let sig = crypto.createHash('sha1').update(content).digest('hex');
    return sig;
};

module.exports.md5 = function (str) {
    let md5sum = crypto.createHash('md5');
    md5sum.update(str);
    str = md5sum.digest('hex');
    return str;
};


module.exports.md5Sign = function (obj, key) {
    //obj.signType="MD5";
    let sign = obj.sign;
    obj.sign = "";
    let createQueryString = function (options) {
        return Object.keys(options).filter(function (key) {
            return options[key] !== undefined && options[key] !== '' && ['pfx', 'apiKey', 'key'].indexOf(key) < 0;
        }).sort().map(function (key) {
            return key + '=' + options[key];
        }).join("&");
    }

    let str = createQueryString(obj);
    obj.sign = sign;
    //console.logg("md5 query str ",str)
    if (key) str += "&key=" + key;

    let md5sum = crypto.createHash('md5');
    md5sum.update(str);
    str = md5sum.digest('hex');
    return str.toUpperCase();
}

module.exports.decodeRsa = function (content, sign, publicKey) {
    let verify = crypto.createVerify('RSA-SHA1');
    verify.update(content);
    let res = verify.verify(publicKey, sign, 'base64');
    return res;
};

module.exports.create = function (uid, timestamp, pwd) {
    let msg = uid + '|' + timestamp;
    let cipher = crypto.createCipher('aes256', pwd);
    let enc = cipher.update(msg, 'utf8', 'hex');
    enc += cipher.final('hex');
    return enc;
};

module.exports.aesEncode = function (str, pwd) {
    let cipher = crypto.createCipher('aes256', pwd);
    let enc = cipher.update(str, 'utf8', 'hex');
    enc += cipher.final('hex');
    return enc;
};

module.exports.aesEncode128 = function (data, pwd) {

    let key = Buffer.from(pwd, 'utf8');
    let v = '1234567890123456'
    //let   v=Buffer.from(v, 'utf8');
    let cipher = crypto.createCipheriv('aes-128-ecb', pwd, '');
    let enc = cipher.update(data, 'utf8', 'hex');

    // let cipher = crypto.createCipher('aes256', pwd);
    // let enc = cipher.update(str, 'utf8', 'hex');
    enc += cipher.final('hex');
    return enc;
};

module.exports.aesDecode128 = function (data, pwd) {

    let key = Buffer.from(pwd, 'utf8');
    let v = '1234567890123456'
    //let   v=Buffer.from(v, 'utf8');
    let cipher = crypto.createDecipheriv('aes-128-ecb', pwd, '');
    let enc = cipher.update(data, 'hex', 'utf8');
    // let cipher = crypto.createCipher('aes256', pwd);
    // let enc = cipher.update(str, 'utf8', 'hex');
    enc += cipher.final('utf8');
    return enc;
};

/**
 *
 * @param {String} str
 * @returns {string}
 */
module.exports.aesEncodeDef = function (str) {
    let pwd = config.conf["session.secret"];
    let cipher = crypto.createCipher('aes256', pwd);
    let enc = cipher.update(str, 'utf8', 'hex');
    enc += cipher.final('hex');
    return enc;
};


module.exports.aesDecode = function (str, pwd) {
    if (!str || str == "undefined")
        return null;
    let decipher = crypto.createDecipher('aes256', pwd);
    let dec;
    try {
        dec = decipher.update(str, 'hex', 'utf8');
        dec += decipher.final('utf8');
    } catch (err) {
        console.error('[token 47] fail to decrypt token.', str);
        return null;
    }
    return dec;
};

module.exports.aesDecodeDef = function (str) {
    let dec = this.aesDecode(str, config.conf["session.secret"]);
    return dec;
};

/**
 * Parse token to validate it and get the uid and timestamp.
 *
 * @param  {String} token token string
 * @param  {String} pwd   decrypt password
 * @return {Object}  uid and timestamp that exported from token. null for illegal token.
 */
module.exports.parse = function (token, pwd) {
    let decipher = crypto.createDecipher('aes256', pwd);
    let dec;
    try {
        dec = decipher.update(token, 'hex', 'utf8');
        dec += decipher.final('utf8');
    } catch (err) {
        console.error('[token 73] fail to decrypt token. ', JSON.stringify(token));
        return null;
    }
    let ts = dec.split('|');
    if (ts.length !== 2) {
        // illegal token
        return null;
    }
    return { uid: ts[0], timestamp: Number(ts[1]) };
};


module.exports.parseDef = function (token) {
    let pwd = config.conf["session.secret"];
    let decipher = crypto.createDecipher('aes256', pwd);
    let dec;
    try {
        dec = decipher.update(token, 'hex', 'utf8');
        dec += decipher.final('utf8');
    } catch (err) {
        console.error('[token 93] fail to decrypt token. ', JSON.stringify(token));
        return null;
    }
    let ts = dec.split('|');
    if (ts.length !== 2) {
        // illegal token
        return null;
    }
    return { uid: ts[0], timestamp: Number(ts[1]) };
};

module.exports.getMd5 = function (str) {
    return crypto.createHash('md5').update(str).digest("hex");
}


module.exports.randomNum = function (n) {
    let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    let res = "";
    for (let i = 0; i < n; i++) {
        let id = Math.ceil(Math.random() * 9);
        res += chars[id];
    }
    return Number(res);
};


module.exports.randomStr = function (n) {
    let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
    let res = "";
    for (let i = 0; i < n; i++) {
        let id = Math.ceil(Math.random() * 35);
        res += chars[id];
    }
    return res;
};

